package com.qianfeng.iot.tenant.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import cn.hutool.core.lang.UUID;
import com.dc3.common.bean.R;
import com.qianfeng.iot.openfeign.UserClient;
import com.qianfeng.iot.tenant.pojo.Dc3Tenant;
import com.qianfeng.iot.tenant.pojo.Dc3TenantBind;
import com.qianfeng.iot.tenant.pojo.Dc3User;
import com.qianfeng.iot.tenant.service.TenantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.List;

/**
 * Created by Jackiechan on 2022/7/14 10:03
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
@Transactional
public class TenantServiceImpl implements TenantService {

    private R2dbcEntityTemplate r2dbcEntityTemplate;

    @Autowired
    public void setR2dbcEntityTemplate(R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    private UserClient userClient;


    @Autowired
    public void setUserClient(UserClient userClient) {
        this.userClient = userClient;
    }

    @Override
    public Mono<R<Object>> addTenant(Mono<Dc3Tenant> tenantMono) {
        //怎么写?
        // r2dbcEntityTemplate.insert(Dc3Tenant.class).into("").using(tenant).var
//        tenantMono.filter(tenant -> StringUtils.hasText(tenant.getName())&&StringUtils.hasText(tenant.getDescription()))
//                .doOnNext(tenant -> {
//                    System.err.println("还有数据:"+tenant);
//                    r2dbcEntityTemplate.insert(Dc3Tenant.class).into("").using(tenant).subscribe();
//                }).hasElement().subscribe(bool->{
//                    if (!bool) {
//                        throw new RuntimeException("数据不完整");
//                     }
//                });
        //我们需要对数据进行校验
        //校验的方式有断言,抛出异常
        //我们通过响应式编程来判断, 响应式编程我们好像没有发现有断言功能
        //我们就转换思路,先过滤,因为过滤后如果不符合条件就没有数据,没有数据我们后面的操作都不执行,都不执行就是一个空的mono
        //如果是空的mono就代表着一定是数据有问题,我们就一切按照正常有数据的方式,最后设置一个如果为空就返回什么数据

        return tenantMono
                .filter(tenant -> StringUtils.hasText(tenant.getName()) && StringUtils.hasText(tenant.getDescription()))
                .doOnNext(tenant -> {
                    tenant.setId(UUID.randomUUID().toString(true));
                })
                .flatMap(tenant -> r2dbcEntityTemplate.insert(Dc3Tenant.class).into("dc3_tenant").using(tenant).map(result -> R.ok())
                ).defaultIfEmpty(R.fail("数据不能为空"));
    }

    @Override
    public Mono<Dc3Tenant> findById(String id) {
        //我们是通过路径参数传递的数据,所以一定是有id
        return r2dbcEntityTemplate.selectOne(Query.query(Criteria.where("id").is(id)), Dc3Tenant.class);
    }

    @Override
    public Flux<Dc3Tenant> findByName(String name) {
        return r2dbcEntityTemplate
                // .select(Query.query(Criteria.where("name").is(name)), Dc3Tenant.class);
                .select(Dc3Tenant.class).from("dc3_tenant").matching(Query.query(Criteria.where("name").is(name))).all();
    }

    @Override
    public Mono<R<Object>> updateDenant(Mono<Dc3Tenant> tenantMono) {
        //更新必须传递id,同时 名字和介绍至少传一个
        return tenantMono.filter(tenant -> StringUtils.hasText(tenant.getId())
                        && (StringUtils.hasText(tenant.getName()) || StringUtils.hasText(tenant.getDescription())))
                //我们想再过滤一下,通过先查询有没有结果来决定
                .flatMap(tenant -> {//利用数据的转换来实现过滤效果
                    //先查询数据,转成查询后的数据
                    return findById(tenant.getId()) //查询数据,如果查询不到,后面的操作都不会执行
                            .map(tenant1 -> { //如果查询到了数据就会执行,如果没有查询到map就不会执行,而是返回空的mono
//                        tenant1.setName(tenant.getName());
//                        tenant1.setDescription(tenant.getDescription());
//                        return tenant1;
                                return tenant;//返回原始数据,因为我们后面更新需要用到原始数据
                            });

                }).flatMap(tenant -> {//更新操作,将传递过来的数据更新到数据库中,前面的数据如果是空的mono,这里就不会执行
                    //  Update update = null;
                    //我们需要根据数据存在与否来决定更新什么
                    HashMap<SqlIdentifier, Object> hashMap = new HashMap<>();
                    if (StringUtils.hasText(tenant.getName())) {
                        hashMap.put(SqlIdentifier.unquoted("name"), tenant.getName());
                        // update = Update.update("name", tenant.getName());
                    }
                    if (StringUtils.hasText(tenant.getDescription())) {
                        hashMap.put(SqlIdentifier.unquoted("description"), tenant.getDescription());
                        // Update.update("name", tenant.getName());
                    }
                    return r2dbcEntityTemplate
                            .update(Dc3Tenant.class)
                            .inTable("dc3_tenant")
                            .matching(Query.query(Criteria.where("id").is(tenant.getId())))
                            .apply(Update.from(hashMap)).map(integer -> {
                                if (integer == 1) {
                                    return R.ok();
                                }
                                return R.fail("数据不存在");
                            });

                }).defaultIfEmpty(R.fail("数据不存在"));
    }

    @Override
    public Mono<Dc3Tenant> findWithUsers(String tenantId) {
        //tenantId转成租户
        return findById(tenantId)//根据id查询租户
                .flatMap(tenant -> {
                    //根据租户的id查询用户id
                    return r2dbcEntityTemplate.select(Dc3TenantBind.class).from("dc3_tenant_bind")
                            .matching(Query.query(Criteria.where("tenant_id").is(tenantId)))////设置查询条件
                            .all()//获取所有返回结果,就是我们所有的数据
                            .map(Dc3TenantBind::getUserId)//只要返回数据中的user_id
                            .collectList()//将id放到一个list中,
                            //根据所有的用户id查询用户数据
                            .flatMap(userIdis -> userClient.findByIdIn(userIdis))//根据所有的id查询到所有的用户
//                            .flatMap(r -> {
//                                tenant.setUserList((List<Dc3User>) r.getData());
//                                return Mono.just(tenant);
//                            })
                            .map(r -> {
                                tenant.setUserList((List<Dc3User>) r.getData());
                                return tenant;
                            });
                });
        //转换为用户数据
    }

    @Override
    public Mono<Integer> findCountByTenantIdAndUserId(String tenantId, List<String> userIds) {
        return r2dbcEntityTemplate.select(Dc3TenantBind.class).from("dc3_tenant_bind")
                .matching(Query.query(Criteria.where("tenant_id").is(tenantId).and(Criteria.where("user_id").in(userIds))))
                .all()
                .collectList()//转成mono
                .map(dc3TenantBinds -> dc3TenantBinds.size());//这个依赖有bug,count会用到count(*) 导致无法映射*

        // return r2dbcEntityTemplate.count(Query.query(Criteria.where("tenant_id").is(tenantId).and(Criteria.where("user_id").in(userIds))), Dc3TenantBind.class);
    }

    @Override
    public Mono<R<Object>> bindUser2Tenant(Dc3TenantBind dc3TenantBind) {
        //设置id
        //这个绑定操作是没有对数据进行重复校验的,也就是不断的请求会不断的添加,这样会带来非幂等性的问题
        //我们理论上需要先查询有没有绑定关系,再决定是不是绑定,减少数据的重复,保证绑定的幂等性
        dc3TenantBind.setId(UUID.randomUUID().toString(true));
        return r2dbcEntityTemplate
                .insert(Dc3TenantBind.class)
                .into("dc3_tenant_bind")
                .using(dc3TenantBind)
                .map(dc3TenantBind1 -> R.ok());
    }

}
